/*
* Copyright 2014 University of Southern California
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package edu.usc.pgroup.floe.processes.cpuaffinity.impl;
import com.sun.jna.LastErrorException;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
import com.sun.jna.PointerType;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.LongByReference;
import edu.usc.pgroup.floe.processes.cpuaffinity.Affinity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implementation of {@link Affinity} for Posix.
* sched_setaffinity(3)/sched_getaffinity(3) from 'c' library.
* Applicable to most linux/unix platforms.
* See: {https://github.com/OpenHFT/Java-Thread-Affinity}
* @author kumbhare
*/
public enum PosixJNAAffinity implements Affinity {
/**
* Singleton instance of the PosixJNAAffinity Class.
* TODO: See if we should use this for others too.
*/
INSTANCE;
/**
* Library name to be loaded (for windows or nix).
*/
private static final String LIBRARY_NAME =
Platform.isWindows() ? "msvcrt" : "c";
/**
* the global logger instance.
*/
private static final Logger LOGGER =
LoggerFactory.getLogger(PosixJNAAffinity.class);
/**
* true if the library using JNA was loaded correctly, false otherwise.
*/
public static final boolean LOADED;
/**
* Byte size. to be used while calculating the size parameter to get/set
* affinity.
*/
private static final int BYTE_SIZE = 8;
/**
* Default Mask for LONG.
*/
private static final long LONG_MASK = 0xFFFFFFFFL;
/**
* MAX cores for long mask.
*/
private static final long LONG_MAX_CORES = 64;
/**
* MAX cores for int mask.
*/
private static final long INT_MAX_CORES = 32;
/**
* Special mask error, raised when mask of wrong size is used (depending
* on the number of cpus in the system).
*/
private static final int MASK_ERROR = 22;
/**
* Returns the affinity mask using JNA call to the clibrary.
* @param pid pid of the process to get the affinity mask. 0 implies,
* current thread/process.
* @return the affinity mask.
*/
@Override
public long getAffinity(final int pid) {
final CLibrary lib = CLibrary.INSTANCE;
//TODO: for systems with 64+ cores...
final LongByReference cpuset = new LongByReference(0L);
try {
final int ret = lib.sched_getaffinity(pid,
Long.SIZE / BYTE_SIZE, cpuset);
if (ret < 0) {
throw new IllegalStateException("sched_getaffinity(("
+ Long.SIZE / BYTE_SIZE + ") , &("
+ cpuset + ") ) return " + ret);
}
return cpuset.getValue();
} catch (LastErrorException e) {
if (e.getErrorCode() != MASK_ERROR) {
throw new IllegalStateException("sched_getaffinity(("
+ Long.SIZE / BYTE_SIZE + ") , &(" + cpuset
+ ") ) errorNo=" + e.getErrorCode(), e);
}
}
final IntByReference cpuset32 = new IntByReference(0);
try {
final int ret = lib.sched_getaffinity(pid,
Integer.SIZE / BYTE_SIZE, cpuset32);
if (ret < 0) {
throw new IllegalStateException("sched_getaffinity(("
+ Integer.SIZE / BYTE_SIZE + ") , &("
+ cpuset32 + ") ) return " + ret);
}
return cpuset32.getValue() & LONG_MASK;
} catch (LastErrorException e) {
throw new IllegalStateException("sched_getaffinity(("
+ Integer.SIZE / BYTE_SIZE + ") , &("
+ cpuset32 + ") ) errorNo="
+ e.getErrorCode(), e);
}
}
/**
* Tries to set the affinity mask using JNA call to the clibrary.
* @param pid pid of the process to set the affinity mask. 0 implies,
* current thread/process.
* @param affinity sets affinity mask of current thread to specified value
*/
@Override
public void setAffinity(final int pid, final long affinity) {
int procs = Runtime.getRuntime().availableProcessors();
if (procs < LONG_MAX_CORES && (affinity & ((1L << procs) - 1)) == 0) {
throw new IllegalArgumentException("Cannot set zero affinity");
}
final CLibrary lib = CLibrary.INSTANCE;
try {
//fixme: for systems with more then 64 cores...
final int ret = lib.sched_setaffinity(pid, Long.SIZE / BYTE_SIZE,
new LongByReference(affinity));
if (ret < 0) {
throw new IllegalStateException("sched_setaffinity(("
+ Long.SIZE / BYTE_SIZE + ") , &(" + affinity
+ ") ) return " + ret);
}
} catch (LastErrorException e) {
if (e.getErrorCode() != MASK_ERROR
|| (affinity & LONG_MASK) != affinity) {
throw new IllegalStateException("sched_setaffinity(("
+ Long.SIZE / BYTE_SIZE + ") , &(" + affinity
+ ") ) errorNo=" + e.getErrorCode(), e);
}
}
if (procs < INT_MAX_CORES && (affinity & ((1L << procs) - 1)) == 0) {
throw new IllegalArgumentException("Cannot set zero "
+ "affinity for 32-bit set affinity");
}
final IntByReference cpuset32 = new IntByReference(0);
cpuset32.setValue((int) affinity);
try {
final int ret = lib.sched_setaffinity(pid,
Integer.SIZE / BYTE_SIZE, cpuset32);
if (ret < 0) {
throw new IllegalStateException("sched_setaffinity(("
+ Integer.SIZE / BYTE_SIZE + ") , &("
+ Integer.toHexString(cpuset32.getValue())
+ ") ) return " + ret);
}
} catch (LastErrorException e) {
throw new IllegalStateException("sched_setaffinity(("
+ Integer.SIZE / BYTE_SIZE + ") , &("
+ Integer.toHexString(cpuset32.getValue())
+ ") ) errorNo=" + e.getErrorCode(), e);
}
}
/**
* @author BegemoT
*/
interface CLibrary extends Library {
/**
* Pointer to the CLibrary.
*/
CLibrary INSTANCE = (CLibrary)
Native.loadLibrary(LIBRARY_NAME, CLibrary.class);
/**
* The native set affinity function.
* @param pid if of the process/thread.
* @param cpusetsize size of the cpuset flag (in bytes)
* @param cpuset the cpuset mask to be used for the process.
* @return 0 if no error, otherwise non-zero error code.
* @throws LastErrorException wrapper for the exception from the library
*/
int sched_setaffinity(final int pid,
final int cpusetsize,
final PointerType cpuset)
throws LastErrorException;
/**
* The native get affinity function.
* @param pid if of the process/thread.
* @param cpusetsize size of the cpuset flag (in bytes)
* @param cpuset the cpuset mask to be used for the process.
* @return 0 if no error, other wise a non-zero error code.
* @throws LastErrorException wrapper for the exception from the library
*/
int sched_getaffinity(final int pid,
final int cpusetsize,
final PointerType cpuset)
throws LastErrorException;
/* CURRENTLY NOT USED.
int sched_getcpu() throws LastErrorException;
int getpid() throws LastErrorException;
int syscall(int number, Object... args) throws LastErrorException;*/
}
static {
boolean loaded = false;
try {
INSTANCE.getAffinity(0);
loaded = true;
LOGGER.info("Loaded the JNA library successfully.");
} catch (UnsatisfiedLinkError e) {
LOGGER.warn("Unable to load jna library {}", e);
}
LOADED = loaded;
}
}